#include <linux/types.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/kernel.h>

#define KEY0_VALUE 0XF0
#define KEY0_INV_VALUE 0X00

#define DEVICE_NAME "key_voltage"
#define DEVICE_MINOR 0
#define DEVICE_MAJOR 240
#define KEY_CNT 0

struct key_read_dev{
    
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    int major;
    int minor;
    struct device_node *nd;
    int key0_gpio;
    atomic_t key_value;

    /* data */
};

struct key_read_dev key_dev;

static int key_open(void){
     int ret;
    const char *str;
    key_dev.nd = of_find_node_by_path("/key");
    // if(ret < 0 || key_dev.nd == NULL){
    //     printk("/key not found - of_find_node_by_path error\n");
    //     return -EINVAL;
    // }
    if(key_dev.nd == NULL){
        printk("/key not found - of_find_node_by_path error\n");
        return -EINVAL;
    }
    ret = of_property_read_string(key_dev.nd,"status",&str);
    if(ret < 0){
        printk("status not found - of_property_read_string error\n");
        return -EINVAL;
    }
    if (strcmp(str,"okay")!= 0)
    {
        return -EINVAL;
    }

    ret = of_property_read_string(key_dev.nd,"compatible",&str);
    if(ret < 0){
        printk("compatible not found - of_property_read_string error\n");
        return -EINVAL;
    }
    if(strcmp(str,"wlz,key") != 0){
        printk("compatible not match - of_property_read_string error\n");
        return -EINVAL;
    }

   key_dev.key0_gpio = of_get_named_gpio(key_dev.nd,"key0-gpios",0);
    if(key_dev.key0_gpio < 0){
        printk("key0-gpios not found - of_get_named_gpio error\n");
        return -EINVAL;
    }
    printk("key0_gpio = %d\n",key_dev.key0_gpio);

    ret = gpio_request(key_dev.key0_gpio,"key0");
    if(ret != 0){
        printk(KERN_ERR "key0-gpios request failed - gpio_request error\n");
        return ret;
    }
    ret = gpio_direction_input(key_dev.key0_gpio);
    if(ret < 0){
        printk("key0-gpios direction input failed - gpio_direction_input error\n");
        return ret;
    }
    return ret;
}

static int key_read_open(struct inode *inode, struct file *filp){
    int ret = 0;
    printk("key_read_open\n");
    
    filp->private_data = &key_dev;
    ret = key_open();
    if(ret < 0){
        printk("key_open error\n");
        return ret;
    }
    return 0;
}
static ssize_t key_read_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){
    int ret;
    int key_value;
    struct key_read_dev *key_dev = filp->private_data ;
    if (gpio_get_value(key_dev->key0_gpio) == 1)
    {
        while (gpio_get_value(key_dev->key0_gpio) == 1);
        atomic_set(&key_dev->key_value,KEY0_VALUE);
    }else{
        atomic_set(&key_dev->key_value,KEY0_INV_VALUE);
    }
    key_value = atomic_read(&key_dev->key_value);
    ret = copy_to_user(buf,&key_value,size);
    return ret;
    
}

static int key_read_release(struct inode *inode, struct file *filp){
    
    struct key_read_dev *key_dev = filp->private_data ;
    printk("key_read_release\n");
    gpio_free(key_dev->key0_gpio);
    return 0;
}

static struct file_operations key_read_fops = {
   .owner = THIS_MODULE,
   .open = key_read_open,
   .read = key_read_read,
   .release = key_read_release,
};



static int __init  key_read_init(void){
   
    int ret = 0;
    printk("key_read_init\n");
    key_dev.key_value = (atomic_t)ATOMIC_INIT(0);
    atomic_set(&key_dev.key_value,KEY0_INV_VALUE);

    if (key_dev.major){
        key_dev.devid = MKDEV(key_dev.major,DEVICE_MINOR);
        ret = register_chrdev_region(key_dev.devid,KEY_CNT,DEVICE_NAME);
        if (ret < 0)
        {
            pr_err("cannot register %s char driver [ret=%d]\n",DEVICE_NAME, KEY_CNT);
            return -EIO;
        }
        
    }else{
        ret = alloc_chrdev_region(&key_dev.devid,DEVICE_MINOR,KEY_CNT,DEVICE_NAME);
        if(ret < 0){
            pr_err("cannot alloc %s char driver [ret=%d]\n",DEVICE_NAME, KEY_CNT);
            return -EIO;
        }
        key_dev.major = MAJOR(key_dev.devid);
        key_dev.minor = MINOR(key_dev.devid);
    }
    printk("major = %d , minor = %d",key_dev.major,key_dev.minor);
    
    key_dev.cdev.owner = THIS_MODULE;
    cdev_init(&key_dev.cdev,&key_read_fops);
    ret = cdev_add(&key_dev.cdev,key_dev.devid,KEY_CNT);
    if(ret < 0){
        pr_err("cannot add %s char driver [ret=%d]\n",DEVICE_NAME, KEY_CNT);
        goto del_unregister;
    }
    key_dev.class = class_create(THIS_MODULE,DEVICE_NAME);
    if(IS_ERR(key_dev.class)){
        pr_err("cannot create %s char driver [ret=%d]\n",DEVICE_NAME, KEY_CNT);
        goto del_cdev;
    }
    key_dev.device = device_create(key_dev.class,NULL,key_dev.devid,NULL,DEVICE_NAME);
    if(IS_ERR(key_dev.device)){
        pr_err("cannot create %s char driver [ret=%d]\n",DEVICE_NAME, KEY_CNT);
        goto del_class;
    }
    return 0;

del_class:
    class_destroy(key_dev.class);
del_cdev:
    cdev_del(&key_dev.cdev);
del_unregister:
    unregister_chrdev_region(key_dev.devid,KEY_CNT);
    return -EIO;
}


static void __exit key_read_exit(void){
    printk("key_read_exit\n");
    cdev_del(&key_dev.cdev);
    unregister_chrdev_region(key_dev.devid,KEY_CNT);
    device_destroy(key_dev.class,key_dev.devid);
    class_destroy(key_dev.class);

    
}
module_init(key_read_init);
module_exit(key_read_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wang");
MODULE_INFO(intree,"Y");

